package com.leyou.search.service;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.leyou.common.pojo.PageResult;
import com.leyou.item.bo.SpuBo;
import com.leyou.item.pojo.Brand;
import com.leyou.item.pojo.Sku;
import com.leyou.item.pojo.SpecParam;
import com.leyou.item.pojo.SpuDetail;
import com.leyou.search.feign.Brandclient;
import com.leyou.search.feign.Categoryclient;
import com.leyou.search.feign.Goodsclient;
import com.leyou.search.feign.Specclient;
import com.leyou.search.pojo.Goods;
import com.leyou.search.pojo.SearchRequest;
import com.leyou.search.pojo.Searchresult;
import com.leyou.search.repository.GoodsRepository;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.LongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.lang.reflect.Array;
import java.util.*;

/**
 * @author shkstart
 * @create 2020-04-22 19:04
 */
@Service
public class GoodsService {
    @Autowired
    Brandclient brandclient;
    @Autowired
    Categoryclient categoryclient;
    @Autowired
    Goodsclient goodsclient;
    @Autowired
    Specclient specclient;
    @Autowired
    GoodsRepository goodsRepository;

    private static final ObjectMapper MAPPER = new ObjectMapper();

    public Goods buildGoods(SpuBo s) throws IOException {
        //获取品牌名
        Brand brand = brandclient.getBrandByBrandId(s.getBrandId());
        String brandName = brand.getName();
        //获取分类名字
        List<String> categoryNames = categoryclient.getCategoryNames(Arrays.asList(s.getCid1(), s.getCid2(), s.getCid3()));
        String caNames = StringUtils.join(categoryNames, " ");
        //获取spu表的title
        String title = s.getTitle();

        //获取sku
        List<Sku> listSku = goodsclient.getListSku(s.getId());
        //吧价格从sku里面单另拿出来存入下面集合
        List<Long> prices = new ArrayList<>();
        //将sku的集合转换成goods对象所需要的json格式的数据类型  存入所需要的字段数据  增加效率节省时间
        List<Map<String, Object>> maps = new ArrayList<>();
        //循环将价格和需要的字段赋值给对应的集合
        listSku.forEach(sku->{
            prices.add(sku.getPrice());
            Map<String, Object> map = new HashMap<>();
            map.put("id",sku.getId());
            map.put("title",sku.getTitle());
            map.put("price",sku.getPrice());

            //判断images是否为空 不为空了采取切割
            String images = sku.getImages();
            String image ="";
            if (StringUtils.isNotBlank(images)){
                image = images.split(",")[0];
            }

            map.put("image",image);
            maps.add(map);
        });
        //将sku集合转换成json格式存入goods对象里面
        String skuString = "";
        try {
            skuString = MAPPER.writeValueAsString(maps);
        }catch (Exception e){
        }

        //查找spec规格参数
        Map<String,Object> specMap = new HashMap<>();
        List<SpecParam> specParams = specclient.getSpecParam(null, s.getCid3(), true, null);
        //根据spuId来获取商品详情
        SpuDetail spuDetail = goodsclient.getSpuDetail(s.getId());

        //通过spuDetail来获取spu和sku的规格参数
        //spu规格参数    将json数据转化为Java对象  方便操作
        String genericSpec = spuDetail.getGenericSpec();
        Map<Long,Object> spuSpec = MAPPER.readValue(genericSpec, new TypeReference<Map<Long, Object>>(){});

        //sku规格参数   将json数据转化为Java对象  方便操作
        String specialSpec = spuDetail.getSpecialSpec();
        Map<Long,List<Object>> skuSpec = MAPPER.readValue(specialSpec, new TypeReference<Map<Long,List<Object>>>(){});

        //通过循环查选出spu和sku
        specParams.forEach(specParam->{
            //判断是否是sku通用属性 就是spu属性
            if (specParam.getGeneric()){
                //spu属性
                //根据param表里面的id查询到detail表里面的generic_spec字段对应json数据的值
                String value = spuSpec.get(specParam.getId()).toString();

                //判断字段是否是数字类型   因为是数字类型要区分区间  对他进行转换
                if (specParam.getNumeric()){
                    value = chooseSegment(value,specParam);
                }
                specMap.put(specParam.getName(),value);
            }else {
                //sku属性
                List<Object> specialSpecs = skuSpec.get(specParam.getId());
                specMap.put(specParam.getName(),specialSpecs);
            }
        });

        //准备构建Goods对象
        Goods goods = new Goods();
        goods.setId(s.getId());
        goods.setAll(title+" "+brandName+" "+caNames);
        goods.setBrandId(s.getBrandId());
        goods.setCid1(s.getCid1());
        goods.setCid2(s.getCid2());
        goods.setCid3(s.getCid3());
        goods.setCreateTime(s.getCreateTime());
        goods.setSubTitle(s.getSubTitle());
        goods.setPrice(prices);
        goods.setSkus(skuString);
        goods.setSpecs(specMap);

        return goods;
    }




    private String chooseSegment(String value, SpecParam p) {
        double val = NumberUtils.toDouble(value);
        String result = "其它";
        // 保存数值段
        for (String segment : p.getSegments().split(",")) {
            String[] segs = segment.split("-");
            // 获取数值范围
            double begin = NumberUtils.toDouble(segs[0]);
            double end = Double.MAX_VALUE;
            if(segs.length == 2){
                end = NumberUtils.toDouble(segs[1]);
            }
            // 判断是否在范围内
            if(val >= begin && val < end){
                if(segs.length == 1){
                    result = segs[0] + p.getUnit() + "以上";
                }else if(begin == 0){
                    result = segs[1] + p.getUnit() + "以下";
                }else{
                    result = segment + p.getUnit();
                }
                break;
            }
        }
        return result;
    }

    public Searchresult searchByKey(SearchRequest searchRequest) {
        //获取到key的值  并进行判断  如果为空直接返回
        String key = searchRequest.getKey();
        if (StringUtils.isBlank(key)){
            return null;
        }
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();

        QueryBuilder myMactchQuery = QueryBuilders.matchQuery("all", key).operator(Operator.AND);

        BoolQueryBuilder boolQueryBuilder = builderByQuerySearch(myMactchQuery,searchRequest);

        nativeSearchQueryBuilder.withQuery(boolQueryBuilder);

        //结果过滤
        nativeSearchQueryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{"id","skus","subTitle"},null));

        //进行分页查询
        int page = searchRequest.getPage()-1;
        int size = searchRequest.getSize();
        nativeSearchQueryBuilder.withPageable(PageRequest.of(page,size));

        //进行聚合获取规格参数
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("categorys").field("cid3"));
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("brands").field("brandId"));

        //进行最终查询
        AggregatedPage<Goods> search = (AggregatedPage<Goods>)goodsRepository.search(nativeSearchQueryBuilder.build());

        /*PageResult<Goods> goodsPageResult = new PageResult<>();

        goodsPageResult.setItems(search.getContent());
        goodsPageResult.setTotal(search.getTotalElements());
        goodsPageResult.setTotalPage(new Long(search.getTotalPages()));*/

        List<Map<String,Object>> categorys = getCategoryByAggSearch(search.getAggregation("categorys"));
        List<Brand> brands = getBrandByAggSearch(search.getAggregation("brands"));

        List<Map<String,Object>> specs = getSpecByAggSearch((Long) categorys.get(0).get("id"),myMactchQuery);

        Searchresult searchresult = new Searchresult();
        searchresult.setItems(search.getContent());
        searchresult.setTotal(search.getTotalElements());
        searchresult.setTotalPage(new Long(search.getTotalPages()));
        searchresult.setCategorys(categorys);
        searchresult.setBrands(brands);
        searchresult.setSpecs(specs);
        return searchresult;
    }

    private BoolQueryBuilder builderByQuerySearch(QueryBuilder myMactchQuery, SearchRequest searchRequest) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(myMactchQuery);
        Map<String, String> filter = searchRequest.getFilter();
        Set<Map.Entry<String, String>> entries = filter.entrySet();
        entries.forEach(e->{
            String key = e.getKey();
            if (key.equals("分类")){
                key = "cid3";
            }else if (key.equals("品牌")){
                key = "brandId";
            }else {
                key = "specs."+key+".keyword";
            }
            String value = e.getValue();
            boolQueryBuilder.filter(QueryBuilders.termQuery(key,value));
        });
        return boolQueryBuilder;
    }

    private List<Map<String, Object>> getSpecByAggSearch(Long cid3, QueryBuilder myMactchQuery) {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        nativeSearchQueryBuilder.withQuery(myMactchQuery);

        List<SpecParam> specParam = specclient.getSpecParam(null, cid3, true, null);

        specParam.forEach(s->{
            String name = s.getName();
            nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms(name).field("specs."+name+".keyword"));
        });
        //纯粹聚合  不包含任何字段
        nativeSearchQueryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{},null));

        AggregatedPage<Goods> goodsByAggPage = (AggregatedPage<Goods>)goodsRepository.search(nativeSearchQueryBuilder.build());

        List<Map<String,Object>> list = new ArrayList<>();
        //得到所有聚合结果  并转换为map集合
        Map<String, Aggregation> stringAggregationMap = goodsByAggPage.getAggregations().asMap();

        Set<Map.Entry<String, Aggregation>> entries = stringAggregationMap.entrySet();
        entries.forEach(entry->{
            Map<String,Object> map = new HashMap<>();
            map.put("key",entry.getKey());

            List<Object> options = new ArrayList<>();

            StringTerms terms = (StringTerms)entry.getValue();
            List<StringTerms.Bucket> buckets = terms.getBuckets();
            buckets.forEach(b->{
                options.add(b.getKeyAsString());
            });
            map.put("options",options);

            list.add(map);
        });
        return list;
    }

    private List<Brand> getBrandByAggSearch(Aggregation aggregation) {
        LongTerms longTerms = (LongTerms)aggregation;
        List<LongTerms.Bucket> buckets = longTerms.getBuckets();
        List<Brand> brands = new ArrayList<>();
        buckets.forEach(b->{
            long l = b.getKeyAsNumber().longValue();
            Brand brandByBrandId = brandclient.getBrandByBrandId(l);
            brands.add(brandByBrandId);
        });
        return brands;
    }

    private List<Map<String, Object>> getCategoryByAggSearch(Aggregation aggregation) {
        LongTerms longTerms = (LongTerms)aggregation;
        List<LongTerms.Bucket> buckets = longTerms.getBuckets();
        List<Long> cids = new ArrayList<>();
        buckets.forEach(b->{
            long l = b.getKeyAsNumber().longValue();
            cids.add(l);
        });
        List<String> categoryNames = categoryclient.getCategoryNames(cids);

       List<Map<String,Object>> list = new ArrayList<>();
       for (int i=0;i<cids.size();i++){
          Map<String, Object> map = new HashMap<>();
          map.put("id",cids.get(i));
          map.put("name",categoryNames.get(i));
          list.add(map);
       }

        return list;
    }
}
